package com.ant.backstage.config.shiro;

import com.ant.backstage.config.jwt.JwtToken;
import com.ant.backstage.facade.dto.UserDTO;
import com.ant.backstage.facade.validation.UserConstant;
import com.ant.backstage.utils.RedisUtil;
import com.ant.backstage.utils.ioc.ApplicationContextUtil;
import com.ant.backstage.utils.jwt.JwtUtil;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.subject.Subject;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * token 密码验证比较器 jwt
 *
 * @author zhangyu
 * @date 2021/12/25 22:24
 * @return
 */

public class JwtCredentialsMatcher implements CredentialsMatcher {

    private static Logger logger = LogManager.getLogger(JwtCredentialsMatcher.class);

    //进行验证密码或token
    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        //  AuthenticationInfo info 是我们在JwtRealm中doGetAuthenticationInfo()返回的那个
        Subject subject = SecurityUtils.getSubject();
        UserDTO user = (UserDTO) subject.getPrincipal();
        JwtToken jwtToken = (JwtToken) token;
        //user为null说明未登录
        if (!Optional.ofNullable(user).isPresent()) {
            user = (UserDTO) info.getPrincipals().getPrimaryPrincipal();
            if (user == null) {
                return false;
            }
        }
        String poToken = Optional.ofNullable(jwtToken.getToken()).isPresent() ? jwtToken.getToken() : user.getToken();
        RedisUtil redisUtil = ApplicationContextUtil.getBean(RedisUtil.class);
        String refreshToken = JwtUtil.getClaimByKey(poToken, "refreshToken");
        String time = JwtUtil.getClaimByKey(poToken, "time");

        Map<String, String> claims = new HashMap<>();
        claims.put("uid", user.getUid());
        claims.put("refreshToken", refreshToken);
        claims.put("time", time);
        try {
            JwtUtil.verifyJwt(poToken, user.getJwtSecret(), claims);
            return true;
        } catch (JWTVerificationException e) {
            if (e instanceof TokenExpiredException) {
                //超时查缓存 ，缓存中存在着需要刷新token  jwt 30分钟  redis
                String uid = JwtUtil.getClaimByKey(poToken, "uid");
                boolean b = redisUtil.hasKey(uid + UserConstant.USER_TOKEN_SUFFIX);
                if (b) {
                    JwtAuthenticatingFilter.shouldRefreshToken = true;
                    logger.info("用户登录token已过期，开始刷新token，oldToken={}", jwtToken.getToken());
                    return true;
                } else {
                    logger.error("用户登录token已过期，redis中token也已过期无法刷新，请重新登录", e);
                    return false;
                }
            }
            logger.error("用户登录token已过期，token校验失败", e);
            return false;
        }
    }


}